﻿using System;
using System.Collections.Generic;
using System.Text;
using ITAP.Core.Modelling.Primitives;
using ITAP.Core.Modelling;
using ITAP.Core.Calculations;

namespace ITAP.Core.Utils
{
    public class DxfToModelPrimitiveAdapter
    {

        public static List<IPrimitive> NormalizeContourPrimitives(List<IPrimitive> primitives)
        {
            List<IPrimitive> result = DxfToModelPrimitiveAdapter.RemoveZeroLengthPrimitives(primitives);
            result = DxfToModelPrimitiveAdapter.SortContourPrimitivesEnds(result);
            result = DxfToModelPrimitiveAdapter.CreateAnticlockwiseContourPrimitives(result);
            return result;
        }

        public static List<IPrimitive> RemoveZeroLengthPrimitives(List<IPrimitive> primitives)
        {
            List<IPrimitive> result = new List<IPrimitive>();
            foreach (IPrimitive primitive in primitives)
                if (primitive.Length != 0)
                    result.Add(primitive);

            return result;
        }
        public static List<IPrimitive> SortContourPrimitivesEnds(List<IPrimitive> primitives)
        {

            List<IPrimitive> _primitives = new List<IPrimitive>();
            foreach (IPrimitive _primitive in primitives)
                _primitives.Add(_primitive.Clone() as IPrimitive);

            if (_primitives.Count < 2)
                return _primitives;

            int counter = 0;
            int count = primitives.Count;
            int index = 0;

            IEndedPrimitive prevPrimitive = _primitives[count - 1] as IEndedPrimitive;
            IEndedPrimitive primitive;

            while (counter != count)
            {
                primitive = _primitives[index] as IEndedPrimitive;
                if (!SimpleEqualProvider.IsEqual(primitive.StartPoint, prevPrimitive.EndPoint, 0.01))
                {
                    _primitives[index] = primitive.ExchangeEnds();
                    primitive = _primitives[index] as IEndedPrimitive;
                    counter = 0;
                }
                else
                    counter++;

                prevPrimitive = primitive;
                index++;
                if (index == count)
                    index = 0;
            }

            return _primitives;
        }
        public static List<IPrimitive> CreateAnticlockwiseContourPrimitives(List<IPrimitive> sortedPrimitives)
        {
            double perimeter = 0;
            double offsetPerimeter = 0;
            foreach (IPrimitive primitive in sortedPrimitives)
                perimeter += primitive.Length;

            List<IEndedPrimitive> offsetPrimitives = DxfToModelPrimitiveAdapter.GetOffsetContour(sortedPrimitives, -0.1);
            foreach (IEndedPrimitive primitive in offsetPrimitives)
                offsetPerimeter += primitive.Length;

            if (double.IsNaN(offsetPerimeter))
                throw new Exception("Ошибка чтения контура.");

            List<IPrimitive> result = new List<IPrimitive>();
            if (offsetPerimeter > perimeter)
            {

                foreach (IPrimitive primitive in sortedPrimitives)
                    result.Insert(0, (primitive as IEndedPrimitive).ExchangeEnds());
            }
            else
            {
                foreach (IPrimitive primitive in sortedPrimitives)
                    result.Add(primitive.Clone() as IPrimitive);
            }

            return result;
        }

        public static IPrimitive DxfToModelPrimitive(Atechnology.glassCAD.Shapes.Dxf.Primitive dxfPrimitive)
        {            
            if (dxfPrimitive is Atechnology.glassCAD.Shapes.Dxf.Arc)
            {
                Atechnology.glassCAD.Shapes.Dxf.Arc arc = dxfPrimitive as Atechnology.glassCAD.Shapes.Dxf.Arc;
                ModelPoint center = new ModelPoint(arc.Center.X, arc.Center.Y);
                ModelPoint startPoint = new ModelPoint(arc.Center.X + arc.Radius, arc.Center.Y);
                Angle startAngle = new Angle(arc.Start * Math.PI / 180);
                Angle endAngle = new Angle(arc.End * Math.PI / 180);

                startPoint = ModelCalculations.RotatePoint(startPoint, center, startAngle.Value);
                ModelPoint endPoint = ModelCalculations.RotatePoint(startPoint, center, (endAngle - startAngle).Value);
                ModelPoint centerPoint = ModelCalculations.RotatePoint(startPoint, center, (endAngle - startAngle).Value / 2);
                Circle result = new Circle(startPoint, centerPoint, endPoint);
                return result;
            }
            else if (dxfPrimitive is Atechnology.glassCAD.Shapes.Dxf.Line)
            {
                Atechnology.glassCAD.Shapes.Dxf.Line line = dxfPrimitive as Atechnology.glassCAD.Shapes.Dxf.Line;
                ModelPoint startPoint = new ModelPoint(line.Start.X, line.Start.Y);
                ModelPoint endPoint = new ModelPoint(line.End.X, line.End.Y);
                return new Line(startPoint, endPoint);
            }
            else
                throw new Exception();
        }

        public static List<IPrimitive> DxfToModelPrimitives(Atechnology.glassCAD.Shapes.Dxf.Dxf dxf)
        {
            List<IPrimitive> modelPrimitives = new List<IPrimitive>();
            IEndedPrimitive prevPrimitive = null;

            // Обязательные условия для создания правильной рамки:
            // 1) Последовательность входящих примитивов строго против часовой стрелки, конец одного совпадает
            //    с началом другого.
            // 2) Допускается, что конец и начало примитива могут быть перепутаны, но первый примитив всегда 
            //    должен быть правильным для правильного размещения границ рамки (UpBorder будет "внутри" рамки).
            foreach (Atechnology.glassCAD.Shapes.Dxf.Primitive dxfPrimitive in dxf)
            {
                IPrimitive primitive = DxfToModelPrimitiveAdapter.DxfToModelPrimitive(dxfPrimitive);
                IEndedPrimitive endedPrimitive = primitive as IEndedPrimitive;
                if (prevPrimitive != null && !SimpleEqualProvider.IsEqual(endedPrimitive.StartPoint, prevPrimitive.EndPoint, 0.01)) // !endedPrimitive.StartPoint.Equals(prevPrimitive.EndPoint))
                {
                    IEndedPrimitive exchangeEndsPrimitive = ((IEndedPrimitive)primitive).ExchangeEnds();
                    if (exchangeEndsPrimitive.Length != 0)
                    {
                        prevPrimitive = exchangeEndsPrimitive;
                        modelPrimitives.Add(exchangeEndsPrimitive);
                    }
                }
                else
                {
                    if (endedPrimitive.Length != 0)
                    {
                        prevPrimitive = endedPrimitive;
                        modelPrimitives.Add(endedPrimitive);
                    }
                }
            }
            modelPrimitives = DxfToModelPrimitiveAdapter.NormalizeContourPrimitives(modelPrimitives);
            return modelPrimitives;
        }

        //public static SpacerMachineInfo GetInformationForSpacerMachine(Atechnology.glassCAD.Shapes.Dxf.Dxf dxf, double offsetValue)
        //{
        //    try
        //    {
        //        List<IPrimitive> modelPrimitives = DxfToModelPrimitiveAdapter.DxfToModelPrimitives(dxf);
        //        List<IEndedPrimitive> offsetPrimitives = DxfToModelPrimitiveAdapter.GetOffsetContour(modelPrimitives, offsetValue);
        //        SpacerMachineInfo result = new SpacerMachineInfo(offsetPrimitives);
        //        return result;
        //    }
        //    catch
        //    {
        //        return null;
        //    }
            
        //}

        /// <summary>
        /// Получение контура с нужным смещением. 
        /// </summary>
        /// <param name="spacerContour">Список примитивов, представляющих контур. Примитивы расположены 
        /// так, что составляют контур последовательно, располагаясь против часовой стрелки. Начало следующего 
        /// примитива должно совпадать с концом предыдущего.</param>
        /// <param name="offsetValue">Если смещение больше нуля, то оно будет произведено "наружу" контура, 
        /// иначе - вовнутрь.</param>
        /// <returns>Список примитивов, смещённых наружу или вовнутрь контура.</returns>
        public static List<IEndedPrimitive> GetOffsetContour(List<IPrimitive> spacerContour, double offsetValue)
        {
            List<IEndedPrimitive> offsetContour = new List<IEndedPrimitive>();
            foreach (IPrimitive primitive in spacerContour)
            {
                offsetContour.Add((IEndedPrimitive)primitive.GetOffsetPrimitive(offsetValue));
            }

            for (int i = 0; i < offsetContour.Count; i++)
            {
                IEndedPrimitive endedPrimitive;
                IEndedPrimitive nextEndedPrimitive;
                IEndedPrimitive prevEndedPrimitive;

                endedPrimitive = offsetContour[i] as IEndedPrimitive;
                if (i == 0)
                {
                    nextEndedPrimitive = offsetContour[i + 1] as IEndedPrimitive;
                    prevEndedPrimitive = offsetContour[offsetContour.Count - 1] as IEndedPrimitive;
                }
                else if (i == offsetContour.Count - 1)
                {
                    nextEndedPrimitive = offsetContour[0] as IEndedPrimitive;
                    prevEndedPrimitive = offsetContour[i - 1] as IEndedPrimitive;
                }
                else
                {
                    nextEndedPrimitive = (IEndedPrimitive)offsetContour[i + 1];
                    prevEndedPrimitive = (IEndedPrimitive)offsetContour[i - 1];
                }
                ModelPoint[] prevIntersectionPoints = endedPrimitive.Intersect(prevEndedPrimitive, EndType.Start);
                ModelPoint[] nextIntersectionPoints = endedPrimitive.Intersect(nextEndedPrimitive, EndType.End);
                if(/* prevIntersectionPoints[0] != null */ !ModelCalculations.IsErrorPoint(prevIntersectionPoints[0]))
                    endedPrimitive.StartPoint = prevIntersectionPoints[0];
                if(/*nextIntersectionPoints[0] != null */ !ModelCalculations.IsErrorPoint(nextIntersectionPoints[0]))
                    endedPrimitive.EndPoint = nextIntersectionPoints[0];
            }
            return offsetContour;
        }
    }
}
